package com.wmyg.antifake.sdk;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.springframework.beans.factory.annotation.Value;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;

public class SmartManager {

    @Value("${server.port}")
    private static String serverPort;

    private OrgManager orgManager;

    private FabricManager fabricManager;

    private Logger logger = LogManager.getLogger(SmartManager.class);

    private String orgName;


    //2018-9-5
    private String userName;
    private String codePath;
    private String channelId;


    public SmartManager(String orgName, String userName, String codePath, String channelId, boolean isToInstall) throws Exception {
        this.orgName = orgName;
        if (isToInstall) {
            initToInstall(orgName, userName, codePath, channelId);
        } else {
            initToUse(orgName, userName, codePath, channelId);
            this.userName = userName;
            this.codePath = codePath;
            this.channelId = channelId;
        }
//        initToUse(orgName, userName, codePath, channelId);
        fabricManager = orgManager.useCommonly(orgName.hashCode());
    }

    private void initToInstall(String orgName, String userName, String codePath, String channelId) throws FileNotFoundException {
        orgManager = new OrgManager();
        Yaml yaml = new Yaml();
        ChaincodeConfig chaincodeConfig = (ChaincodeConfig) yaml.load(new FileInputStream(new File("code/src/" + codePath + "config/chaincodeconfig.yaml")));
        int hash = orgName.hashCode();
        orgManager.init(hash, new File("cert/" + orgName + ".yaml"), userName, true)
                .setChannel(channelId)
                .setChainCode(chaincodeConfig.getCodeName(), codePath,
                        chaincodeConfig.getCodeVersion(), "code/", 10000, 10)
                .setBlockListener(map -> {
                    logger.debug(map.get("code"));
                    logger.debug(map.get("data"));
                }).add();
    }

    private void initToUse(String orgName, String userName, String codePath, String channelId) throws FileNotFoundException {
        orgManager = new OrgManager();
        Yaml yaml = new Yaml();
        ChaincodeConfig chaincodeConfig = (ChaincodeConfig) yaml.load(new FileInputStream(new File("code/src/" + codePath + "config/chaincodeconfig.yaml")));
        int hash = orgName.hashCode();
        orgManager.init(hash, new File("cert/" + orgName + ".yaml"), userName, true)
                .setChannel(channelId)
                .setChainCode(chaincodeConfig.getCodeName(), codePath,
                        chaincodeConfig.getCodeVersion(), 10000, 120)//120000
//                .setBlockListener(map -> {
//                    logger.debug(map.get("code"));
//                    logger.debug(map.get("data"));
//                })
                .add();
    }

    public void firstInstall(String[] args) throws Throwable {
        fabricManager = orgManager.use(orgName.hashCode());
        fabricManager.install();
        fabricManager.instantiate(args);
        fabricManager.abandon();
    }

    public void install(String[] args) throws Throwable {
        fabricManager = orgManager.use(orgName.hashCode());
        fabricManager.install();
        fabricManager.upgrade(args);
        fabricManager.abandon();
    }

    public Map<String, String> invoke(String fcn, String[] args) throws Throwable {
//        initToUse(orgName, userName, codePath, channelId);
//        fabricManager = orgManager.useCommonly(orgName.hashCode());
        //System.out.println("ThreadID:" + Thread.currentThread().getId() + " Hash:" + fabricManager.hashCode() + " Smart:" + this.hashCode());
//        StringBuilder sb = new StringBuilder();
//        for (String item : args) {
//            sb.append(item).append(';');
//        }
//        System.out.println("invok fun:" + fcn + "  args:" + sb.toString());
        try {
            Map<String, String> map = fabricManager.invoke(fcn, args);
//        System.out.println(map);
            if ("error".equals(map.get("code"))) {
                logger.error(map.get("data"));
//                fabricManager.abandon();
                throw new FabricException(map.get("cause"));
            }
//        fabricManager.abandon();
            return map;
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
            logger.info("Find the Channel is shutdown, from program of the port " + serverPort);
//            Runtime.getRuntime().exec("nohup /mnt/fabricuploader/fabricuploader.sh " + serverPort + " &");
//            fabricManager = orgManager.useAgain(orgName.hashCode());
            throw e;
        } catch (Throwable throwable) {
            throw throwable;
        }
    }

    public Map<String, String> query(String fcn, String[] keys) throws Throwable {
//        initToUse(orgName, userName, codePath, channelId);
//        FabricManager fabricManager = orgManager.useCommonly(orgName.hashCode());
        Map<String, String> map = fabricManager.query(fcn, keys);
//        fabricManager.abandon();
        return map;
    }

    public void shutdown() throws Throwable {
        fabricManager.abandon();
    }

}
